package com.mimose.component.chains.chain;

import com.mimose.component.chains.entity.request.Request;
import com.mimose.component.chains.entity.response.AbsResponse;
import com.mimose.component.chains.entity.response.Response;
import com.mimose.component.chains.entity.response.ResponseAsyn;
import com.mimose.component.chains.invoker.Invoker;
import com.mimose.component.chains.util.Constants;
import com.mimose.component.chains.config.ChainDog;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;

/**
 * @Description 链路类
 * @Author ccy
 * @Date 2019/10/17 14:11
 */
@Slf4j
public abstract class Chain {

    private static final Character SYNC_N = 'N';
    private static final Character SYNC_Y = 'Y';

    //链路中的处理器
    public Invoker invoker;
    //是否返回异步结果，进行回调
    private volatile Character sync = SYNC_N;
    //异步调用所需线程池
    private volatile ExecutorService executorService;

    /**
     * 为链路设置处理器（需要具体实现）
     * e.g -->
     *  invoker = new ExampleInvoker1();         //设置第一个处理器
     *  invoker.setNext(new ExampleInvoker2());    //设置下一个处理器
     *  invoker.setNext(new ExampleInvoker3());   //设置下一个处理器
     *
     *  setNext增加的处理器将重复使用上个处理器的request，以及返回的response，
     *  可以将需要的信息，在上一个处理器处理过程中进行request和response赋值，从而可以再下一个处理器中继续使用
     */
    public abstract void withInvokers();

    /**
     * 为链路设置一个唯一的名字
     * @return
     */
    public abstract String myName();

    /**
     * 模板方法，通过该方法进行链路中所有处理器的调用
     * @param request
     * @return
     */
    public final AbsResponse invoke(Request request){
        synchronized (sync){
            if(this.sync == SYNC_Y){
                return invokeSync(request);
            }
            return invokeNotSync(request);
        }
    }

    /**
     * 同步进行操作
     * @param request
     * @return
     */
    private final AbsResponse invokeNotSync(Request request) {
        synchronized (sync){
            AbsResponse response = new Response();
            try {
                if(invoker == null){
                    return response;
                }
                Invoker current = invoker;
                do{
                    response = current.invoke0(request.getRequest(), response);
                    current = current.next0();
                }while(current!=null && !response.isDone());
                return response;
            } catch (final Exception e){
                log.error("do invoke error", e);
                return response.end(true, null);
            }
        }
    }

    /**
     * 返回Task进行异步回调
     * @param request
     * @return
     */
    private final AbsResponse invokeSync(Request request) {
        synchronized (sync){
            AbsResponse response = new ResponseAsyn();
            CompletableFuture task = CompletableFuture.supplyAsync(() -> ((Response) invokeNotSync(request)).getResponse(), this.executorService);
            return response.end(true, task);
        }
    }

    private final void clearInvokers(){
        synchronized (sync){
            log.info("clear chain's invokers");
            this.invoker = null;
            this.sync = SYNC_N;
            this.executorService = null;
        }
    }

    /**
     * 获取对应的链路，同时调用具体实现的withInvokers来进行处理器的单例初始化
     * @param chainName
     * @return
     */
    public static Chain newChain(String chainName) {
        Chain chain = null;
        try {
            chain = ChainDog.newChain0(chainName);
            if(chain.invoker==null){
                chain.withInvokers();
            }
            chain.withNotSync();
            return chain;
        } catch (final Exception e){
            log.error("newChain error, return to default chain", e);
            if(chain != null) {
                chain.clearInvokers();
            }
            return ChainDog.newChain0(Constants.DEFAULT_CHAIN_NAME);
        }
    }

    /**
     * 启用异步回调方式
     * @return
     */
    public Chain withSync(){
        synchronized (sync){
            this.sync = SYNC_Y;
            if(this.executorService == null){
                this.executorService = ChainDog.wantExecutor();
            }
            return this;
        }
    }

    private Chain withNotSync(){
        synchronized (sync){
            this.sync = SYNC_N;
            return this;
        }
    }

}
